/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 carryover_hero.cpp

	$Header: /heroes4/carryover_hero.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "carryover_hero.h"

#include <algorithm>

#include "adv_object_type.h"
#include "adventure_map.h"
#include "army.h"
#include "artifact_set.h"
#include "artifact_slot.h"
#include "carryover_data.h"
#include "elements_of.h"
#include "enum_operations.h"
#include "hero_ptr.h"
#include "object_registration.h"
#include "ownable_event.h"
#include "player.h"
#include "sized_int_types.h"
#include "streambuf_operators.h"

// --------------------------------------------------------------------------
// Unnamed namespace
// --------------------------------------------------------------------------

namespace
{

	t_object_registration< t_carryover_hero > g_registration( k_adv_object_carryover_hero );

	// --------------------------------------------------------------------------
	// --------------------------------------------------------------------------
	void strip_invalid_artifacts(
		t_hero_carryover_data &	hero_data,
		t_carryover_data &		carryover_data,
		t_artifact_set const &	mask )
	{
		t_artifact_slot slot;
		for ( slot = t_artifact_slot( 0 ); slot < k_artifact_slot_hero_count; enum_incr( slot ) )
		{
			t_artifact const & artifact = hero_data.get_artifact( slot );
			if ( artifact.get_icon() >= 0 && !mask[ artifact.get_icon() ] )
			{
				// Put it back in the artifact pool
				carryover_data.add( artifact );
				hero_data.set_artifact( slot, t_artifact() );
			}
		}

		typedef t_hero_carryover_data::t_backpack t_backpack;
		t_backpack new_backpack;
		
		t_backpack const & backpack = hero_data.get_backpack();
		t_backpack::const_iterator backpack_end = backpack.end();
		t_backpack::const_iterator backpack_iter = backpack.begin();
		for ( ; backpack_iter != backpack_end; ++backpack_iter )
		{
			t_artifact const & artifact = *backpack_iter;
			if ( !mask[ artifact.get_icon() ] )
			{
				// Put it back in the artifact pool
				carryover_data.add( artifact );
			}
			else
				new_backpack.push_back( artifact );
		}

		hero_data.set_backpack( new_backpack );
	}

} // Unnamed namespace

// --------------------------------------------------------------------------
// t_carryover_hero members
// --------------------------------------------------------------------------

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
t_carryover_hero::t_carryover_hero( std::string const & model_name )
	:	t_stationary_adventure_object( model_name ),
		m_owner_color( k_player_gray ),
		m_patrol_type( k_patrol_none ),
		m_patrol_radius( -1 )
{
	std::fill_n(
		m_built_in_events,
		ELEMENTS_OF( m_built_in_events ),
		t_hero_built_in_event_ptr( new t_hero_built_in_event ) );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
void t_carryover_hero::place(
	t_adventure_map &		map,
	t_adv_map_point const &	point )
{
	t_carryover_data * carryover_data = map.get_carry_out_data();
	// No carryover data, no carryover heroes
	if ( carryover_data == 0 )
		return;

	int owner_number = map.get_player_number( m_owner_color );
	bool human_owner = owner_number >= 0 && !map.get_player( owner_number ).is_computer();

	// Retrieve the hero data from the carryover data
	t_hero_carryover_data_ptr data_ptr;
	if ( !m_hero_name.empty() )
	{
		data_ptr = m_carryover_data_ptr;
		m_carryover_data_ptr = 0;
	}
	else
	{
		if ( owner_number < 0 )
			return;

		if ( human_owner )
			data_ptr = carryover_data->retrieve_most_powerful_hero_for_human();
		else
			data_ptr = carryover_data->retrieve_most_powerful_hero( m_owner_color );
	}
	if ( data_ptr.get() == 0 )
		return;

	strip_invalid_artifacts(
		*data_ptr,
		*carryover_data,
		human_owner ? map.get_carry_in_artifacts() : t_artifact_set() );

	// Create the army
	t_army_ptr new_army_ptr;
	if ( map.is_ocean( point ) )
		new_army_ptr = new t_army( data_ptr->get_alignment() );
	else
		new_army_ptr = new t_army;
	new_army_ptr->set_owner( owner_number );
	new_army_ptr->set_patrol_type( m_patrol_type );
	new_army_ptr->set_patrol_radius( m_patrol_radius );

	// Create the hero
	t_hero_ptr new_hero_ptr( new t_hero( *data_ptr ) );

	new_hero_ptr->set_spell_points( new_hero_ptr->get_maximum_spell_points() );
	new_hero_ptr->set_raw_adventure_movement( new_hero_ptr->get_raw_adventure_movement() );

	new_hero_ptr->set_built_in_events( m_built_in_events );
	new_hero_ptr->take_timed_events( m_timed_events );
	new_hero_ptr->take_triggerable_events( m_triggerable_events );
	new_hero_ptr->take_continuous_events( m_continuous_events );

	// Put the hero into the army
	new_army_ptr->add( new_hero_ptr );

	new_army_ptr->place( map, point );
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_carryover_hero::preplacement( t_adventure_map & map, int pass )
{
	assert( m_carryover_data_ptr.get() == 0 );

	// Never need more than one pass
	if ( pass > 0 || m_hero_name.empty() )
		return false;

	t_carryover_data * carryover_data = map.get_carry_out_data();
	// No carryover data, no carryover heroes
	if ( carryover_data == 0 )
		return false;

	// Retrieve carryover data for named hero
	m_carryover_data_ptr = carryover_data->retrieve_named_hero( m_hero_name );

	return false;
}

// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
bool t_carryover_hero::read_from_map(
	std::streambuf &		stream,
	t_progress_handler *	progress_handler )
{
	int const k_current_format_version = 2;

	int format_version = get< t_uint16 >( stream );
	if ( format_version < 0 || format_version > k_current_format_version )
		return false;

	if ( format_version >= 1 )
	{
		m_owner_color = t_player_color( get< t_uint8 >( stream ) );
		if ( m_owner_color < 0 || m_owner_color >= k_player_color_count )
			return false;

		if ( !read_string16( stream, m_hero_name ) )
			return false;

		if ( format_version >= 2 )
		{
			if ( get< t_int8 >( stream ) != 0 )
			{
				m_patrol_type = static_cast< t_patrol_type >( get< t_int8 >( stream ) + k_patrol_guard );
				m_patrol_radius = get< t_int8 >( stream );
			}

			if (	!read_hero_built_in_events_from_map( stream, m_built_in_events )
				||	!read_hero_timed_events_from_map( stream, m_timed_events )
				||	!read_hero_triggerable_events_from_map( stream, m_triggerable_events )
				||	!read_hero_continuous_events_from_map( stream, m_continuous_events ) )
				return false;
		}
	}

	return true;
}
